package com.zuiacsn.java_gobang.api;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zuiacsn.java_gobang.game.*;
import com.zuiacsn.java_gobang.mapper.UserMapper;
import com.zuiacsn.java_gobang.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;

@Component
public class GameAPI extends TextWebSocketHandler {
    @Autowired
    ObjectMapper objectMapper;
    @Autowired
    RoomManager roomManager;
    @Autowired
    OnlineUserManager onlineUserManager;
    @Autowired
    UserMapper userMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse response = new GameReadyResponse();
        // 1.先获取用户的身份信息
        User user = (User) session.getAttributes().get("user");
        if(user == null){
            // 用户没有登录,直接访问游戏界面
            response.setOk(false);
            response.setReason("用户尚未登录,请返回登录!");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        // 2.判断当前用户是否在房间中
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if(room == null){
            // 当前没有对应的房间,该玩家上没有匹配到
            response.setOk(false);
            response.setReason("用户尚未匹配到!");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        // 3.判断多开
        if(onlineUserManager.getFromGall(user.getUserId()) != null
        || onlineUserManager.getFromRoom(user.getUserId()) != null){
            // 一边在游戏大厅 一边在游戏房间也是多开
            response.setOk(true);
            response.setReason("禁止用户多开!");
            response.setMessage("repeatConnection");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }
        // 4. 设置当前玩家上线
        onlineUserManager.enterGameRoom(user.getUserId(), session);
        // 5. 把两个玩家加入到游戏房间中

        synchronized (room) {
            if(room.getUser1() == null){
                // 第一个玩家尚未加入房间 ,把当前用户加入user1中
                room.setUser1(user);
                room.setWhiteUser(user.getUserId());
                System.out.println("玩家"+user.getUsername()+"已经准备就绪! 作为玩家1");
                return;
            }
            if(room.getUser2() == null){
                room.setUser2(user);
                System.out.println("玩家"+user.getUsername()+"已经准备就绪! 作为玩家2");

                // 当两个玩家都已经成功上线 服务器就要给每个玩家返回一个 websocket message表示gameReady
                // 通知玩家1
                noticeGameReady(room,room.getUser1(),room.getUser2());
                // 通知玩家2
                noticeGameReady(room,room.getUser2(),room.getUser1());
                return;
            }
        }
        // 6.此时如果又有玩家尝试连同一个房间 直接报错 理论上并不存在
        response.setOk(false);
        response.setReason("当前房间已满! 无法加入");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
    }

    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse response = new GameReadyResponse();
        response.setOk(true);
        response.setMessage("gameReady");
        response.setReason("");
        response.setRoomId(room.getRoomId());
        response.setThisUserId(thisUser.getUserId());
        response.setThatUserId(thatUser.getUserId());
        response.setWhiteUser(room.getWhiteUser());
        WebSocketSession session = onlineUserManager.getFromRoom(thisUser.getUserId());
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 1.先从session中拿到当前玩家的身份信息
        User user = (User) session.getAttributes().get("user");
        if(user == null){
            System.out.println("[handleTextMessage] 用户尚未登录!");
            return;
        }
        //2. 根据玩家id获取房间对象
        Room  room = roomManager.getRoomByUserId(user.getUserId());
        //3. 通过 room 对象来处理这次具体的请求
        room.putChess(message.getPayload());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        // 玩家下线 从onlineUserManager中进行删除
        User user = (User) session.getAttributes().get("user");
        if(user == null){
            // 用户没有登陆
            return;
        }
        WebSocketSession tmpSession = onlineUserManager.getFromRoom(user.getUserId());
        if(tmpSession == session){
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        System.out.println("用户"+user.getUsername()+"游戏房间连接异常");
        // 当前玩家掉线, 通知对手获胜!
        noticeThatUserWin(user);
    }


    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        // 玩家下线 从onlineUserManager中进行删除
        User user = (User) session.getAttributes().get("user");
        if(user == null){
            return;
        }
        WebSocketSession tmpSession = onlineUserManager.getFromRoom(user.getUserId());
        if(tmpSession == session){
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        System.out.println("用户"+user.getUsername()+"离开游戏房间");
        // 当前玩家退出页面
        noticeThatUserWin(user);
    }


    private void noticeThatUserWin(User user) throws IOException {
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if(room == null){
            System.out.println("[GameAPI] [noticeThatUserWin] 当前房间已经释放,无需通知对手");
            return;
        }

        User thatUser = room.getUser1() == user ? room.getUser2() : room.getUser1();
        WebSocketSession session = onlineUserManager.getFromRoom(thatUser.getUserId());
        if(session == null){
            // 说明对手也掉线了,没必要通知了
            return;
        }
        // 构造响应, 通知对手获胜
        GameResponse response = new GameResponse();
        response.setWinner(thatUser.getUserId());
        response.setUserId(thatUser.getUserId());
        response.setMessage("putChess");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));

        // 更新数据
        userMapper.userWin(thatUser.getUserId());
        userMapper.userLose(user.getUserId());

        // 释放房间对象
        roomManager.remove(room.getRoomId(),room.getUser1().getUserId(),room.getUser2().getUserId());
    }
}
